import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;

// 
public class Bot implements Runnable {
	HashMap<GameStatus, Node> inputLayer;
	LinkedList<Node> logNode;
	LinkedList<Link> logLink;
	
	public Bot() {
		inputLayer = new HashMap<GameStatus, Node>();
		logNode = new LinkedList<Node>();
		logLink = new LinkedList<Link>();
	}
	
	// start study
	public void study() throws Exception {
		// make a game
		final int MAP_SIZE_X = 3;
		final int MAP_SIZE_Y = 3;
		Game game = new Game(MAP_SIZE_X, MAP_SIZE_Y);
		game.controlPlayer = game.addPlayer('a');
		game.addPlayer('b');
		game.randomPlayer();
		
		// search best answer
		int shortStep = Bot.BFS(game.clone());
		
		// get the map status
		GameStatus nowStatus = game.getGameStatus();
		
		// find suitable node in list
		if (inputLayer.containsKey(nowStatus) == false) {
			// add node in list and make initial link
			Node node = new Node();
			node.status = nowStatus;
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 1));
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 2));
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 3));
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 4));
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 5));
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 6));
			node.link.add(new Link(node, null, Link.DEFAULT_WEIGHT, 7));
			inputLayer.put(node.status, node);
		}
		
		// do a operate
		Node node = inputLayer.get(nowStatus);
		int totalWeight = 0;
		for (Link link : node.link) {
			totalWeight += link.weight;
		}
		int random = new Random().nextInt(totalWeight) + 1;
		Link usedLink = null;
		for (Link link : node.link) {
			random -= link.weight;
			if (random <= 0) {
				try {
					Bot.operate(game, link.operate);
					usedLink = link;
				} catch (Exception e) {
					throw new Exception("link have a error operate!");
				}
			}
		}
		
		// log used node and link
		if (usedLink == null) {
			throw new Exception("usedLink is null!");
		}
		logNode.addLast(node);
		logLink.addLast(usedLink);
		
		// get answer and fix weight
		GameStatus nextStatus = game.getGameStatus();
		if (nextStatus.remainEnemy == 0) {
			// add weight
			
		}
		
	}

	public void run() {
		//TODO: BOT study will running here
		try {
			this.study();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	// search a game's best answer, return a best short step number
	public static int BFS(Game game) {

		// make FIFO list
		LinkedList<Game> listGame = new LinkedList<Game>();
		LinkedList<StringBuffer> listOperate = new LinkedList<StringBuffer>();
		listGame.add(game);
		listOperate.add(new StringBuffer());
		
		// Start BFS search
		System.out.println("Start search the map");
		listGame.getFirst().printMap();
		while (true) {
			Game nowGame = listGame.removeFirst();
			StringBuffer nowOperate = listOperate.removeFirst();
			
			Game nextGame;
			StringBuffer nextOperate;
			
			// search move
			for (int s = 1; s <= 4; s++) {
				try {
					nextGame = nowGame.clone();
					nextOperate = new StringBuffer(nowOperate);
					nextGame.move(nextGame.controlPlayer, s);
					switch (s) {
					case 1:
						nextOperate.append("w");
						break;
					case 2:
						nextOperate.append("d");
						break;
					case 3:
						nextOperate.append("s");
						break;
					case 4:
						nextOperate.append("a");
						break;
					}
					listGame.addLast(nextGame);
					listOperate.addLast(nextOperate);
					// if you want to see more detail
					//System.out.println("now is operate w");
					//nowGame.printMap();
					//nextGame.printMap();
				} catch (Exception e) {}
			}
			// search turn
			for (int s = 1; s <= 2; s++) {
				try {
					nextGame = nowGame.clone();
					nextOperate = new StringBuffer(nowOperate);
					nextGame.turn(nextGame.controlPlayer, s);
					switch (s) {
					case 1:
						nextOperate.append("q");
						break;
					case 2:
						nextOperate.append("e");
						break;
					}
					listGame.addLast(nextGame);
					listOperate.addLast(nextOperate);
					// if you want to see more detail
					//System.out.println("now is operate q");
					//nowGame.printMap();
					//nextGame.printMap();
				} catch (Exception e) {}
			}
			// search attack
			int killed; // sign player killed number
			nextGame = nowGame.clone();
			nextOperate = new StringBuffer(nowOperate);
			killed = nextGame.attack(nextGame.controlPlayer);
			nextOperate.append("k");
			listGame.addLast(nextGame);
			listOperate.addLast(nextOperate);
			// if you want to see more detail
			//System.out.println("now is operate k");
			//nowGame.printMap();
			//nextGame.printMap();

			if (killed > 0) {
				System.out.println("killed " + killed);
				System.out.println(nextOperate);
				System.out.println("now map is");
				nowGame.printMap();
				System.out.println("next map is");
				nextGame.printMap();
				return nextOperate.length();
			}

		}
	}
	
	// use a operate sign and do it, and return the operate will kill number
	public static int operate(Game game, int operate) throws Exception {
		/*
		 * the API mean is
		 * 1 w  2 d  3 s  4 a
		 * 5 q  6 r
		 * 7 k
		 */
		switch (operate) {
		case 1:
			game.move(game.controlPlayer, Player.MOVE_FRONT);
			break;
		case 2:
			game.move(game.controlPlayer, Player.MOVE_RIGHT);
			break;
		case 3:
			game.move(game.controlPlayer, Player.MOVE_BACK);
			break;
		case 4:
			game.move(game.controlPlayer, Player.MOVE_LEFT);
			break;
		case 5:
			game.turn(game.controlPlayer, Player.TURN_LEFT);
			break;
		case 6:
			game.turn(game.controlPlayer, Player.TURN_RIGHT);
			break;
		case 7:
			return game.attack(game.controlPlayer);
		default:
			throw new Exception("API operate error");
		}
		return 0;
	}
	
}